﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Yto.EF.DbCtx;
using Yto.Entities;
using Yto.Linq;
using Yto.Repositories;

namespace Yto.EF.Repositories
{
    public class EFRepository<T, TE, TPK> : BaseRepository<TE, TPK>, IEfDbContext
        where TE : class, IEntity<TPK>
        where T : DbContext
    {
        public T _context => _dbProvider.GetDbContext();
        private IDbProvider<T> _dbProvider;

        public virtual DbSet<TE> Table => _context.Set<TE>();

        public override TE Single(Expression<Func<TE, bool>> expression)
        {
            return GetAll().Single(expression);
        }

        public override IQueryable<TE> GetAll()
        {
            return Table;
        }

        public EFRepository(IDbProvider<T> dbProvider)
        {
            _dbProvider = dbProvider;
        }

        public override TE Insert(TE entity)
        {
            return Table.Add(entity);
        }

        public override TE Update(TE entity)
        {
            AttachEntity(entity);
            _context.Entry(entity).State = EntityState.Modified;
            return entity;

        }

        public override Task<TE> UpdateAsync(TE entity)
        {
            AttachEntity(entity);
            _context.Entry(entity).State = EntityState.Modified;
            return Task.FromResult(entity);
        }

        private void AttachEntity(TE entity)
        {
            if (!Table.Local.Contains(entity))
            {
                Table.Attach(entity);
            }
        }

        public override void Delete(TE entity)
        {
            //_context.Entry(entity).State = EntityState.Deleted;
            //string addr = getMemory(_context);
            //Table.Attach(entity);
            //addr = getMemory(_context);
            AttachEntity(entity);
            Table.Remove(entity);
        }

        public string getMemory(object o) // 获取引用类型的内存地址方法    
        {
            GCHandle h = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);

            IntPtr addr = GCHandle.ToIntPtr(h);

            return "0x" + addr.ToString("X");
        }

        public override void Delete(TPK id)
        {
            //var entity = Table.Local.FirstOrDefault(item => EqualityComparer<TPK>.Default.Equals(item.Id, id));\
            var entity = Table.Find(id);
            if (entity == null)
            {
                return;
            }
            Delete(entity);
        }

        public override List<TE> GetList()
        {
            return GetAll().ToList();
        }

        public override async Task<List<TE>> GetListAsync()
        {
            return await GetAll().ToListAsync();
        }

        public override async Task<List<TE>> GetListAsync(Expression<Func<TE, bool>> expression, int maxCount = 0)
        {
            var queryable = GetAll().Where(expression);
            if (maxCount > 0)
            {
                queryable = queryable.Take(maxCount);

            }
            return await queryable.ToListAsync();
        }

        public override bool BatchInsert(IEnumerable<TE> list)
        {
            int result = -1;
            try
            {
                Table.BulkInsert(list);
                result = _context.SaveChanges();

            }
            catch (Exception ex)
            {
                throw ex;
            }
            return result == list.Count();
        }

        public override bool DeleteAll()
        {
            var attributes = typeof(TE).GetCustomAttributes(typeof(TableAttribute), true)
                .Cast<TableAttribute>()
                .ToArray();
            string tableName = string.Empty;
            if (attributes.Length > 0)
            {
                var item = attributes[0];
                tableName = item.Name;
            }
            else
            {
                tableName = typeof(TE).Name;
            }
;
            var result = _context.Database.ExecuteSqlCommand($"DELETE FROM { tableName }");
            return result > 0;
        }


        public override Task<TE> FirstOrDefaultAsync(TPK id)
        {
            return GetAll().FirstOrDefaultAsync(ExpressionHelper<TE, TPK>.CreateExpressForGetByID(id));
        }

        public override Task<TE> FirstOrDefaultAsync(Expression<Func<TE, bool>> expression)
        {
            return GetAll().FirstOrDefaultAsync(expression);
        }

        public DbContext GetDbContext()
        {
            return _context;
        }
    }

    public class EFRepository<T, TE> : EFRepository<T, TE, int>, IBaseRepository<TE>
        where TE : class, IEntity<int>
        where T : DbContext
    {
        public EFRepository(IDbProvider<T> dbProvider) : base(dbProvider) { }
    }
}
